﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HydroDesktop.Interfaces.ObjectModel;
using System.IO;

namespace DroughtAnalysis
{
    /// <summary>
    /// Stores information about a DROUGHT R-Script
    /// Also reads the description and other information from the R-script
    /// </summary>
    public class DroughtMethodInfo
    {
        private DroughtMethodInfo() 
        {
            TypeOfDrought = DroughtType.Meteo;
            InputVariables = new List<Variable>();
            OutputVariables = new List<Variable>();
            OutputFiles = new List<string>();
            InputFiles = new List<string>();
        }

        /// <summary>
        /// Reads the drought method info from the R-Script
        /// </summary>
        /// <param name="scriptFileName">file name of the R-Script</param>
        /// <returns>information about the drought method</returns>
        public static DroughtMethodInfo FromRScript(string scriptFileName)
        {
            DroughtMethodInfo m = new DroughtMethodInfo();

            //file not found - return an invalid method
            if (!File.Exists(scriptFileName)) return m;

            //default method name is the script name
            m.MethodName = Path.GetFileName(scriptFileName);
            //default description is same as method name
            m.MethodDescription = m.MethodName;
            m.RScriptPath = scriptFileName;

            var inputVariables = new List<string>();
            var inputUnits = new List<string>();
            var inputIntervals = new List<string>();
            var inputColumns = new List<string>();
            var inputFiles = new List<string>();

            var outputVariables = new List<string>();
            var outputUnits = new List<string>();
            var outputIntervals = new List<string>();
            var outputFiles = new List<string>();
            var outputColumns = new List<string>();


            try
            {
                //start reading the file
                using (StreamReader reader = new StreamReader(scriptFileName))
                {
                    string line = String.Empty;
                    while ((line = reader.ReadLine()) != null)
                    {
                        line = line.Trim();
                        
                        if (line.StartsWith("P.type"))
                        {
                            m.TypeOfDrought = (ParseParameter(line) == "hydro") ? DroughtType.Hydro : DroughtType.Meteo;
                        }
                        else if (line.StartsWith("P.name"))
                        {
                            string name = ParseParameter(line);
                            if (!String.IsNullOrEmpty(line))
                                m.MethodName = name;
                        }
                        else if (line.StartsWith("P.description"))
                        {
                            string desc = ParseParameter(line);
                            if (!String.IsNullOrEmpty(line))
                                m.MethodDescription = desc;
                        }
                        else if (line.StartsWith("P.input.variable"))
                        {
                            inputVariables = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.input.unit"))
                        {
                            inputUnits = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.input.interval"))
                        {
                            inputIntervals = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.input.file"))
                        {
                            inputFiles = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.input.column"))
                        {
                            inputColumns = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.output.variable"))
                        {
                            outputVariables = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.output.unit"))
                        {
                            outputUnits = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.output.interval"))
                        {
                            outputIntervals = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.output.file"))
                        {
                            outputFiles = m.ParseArray(line);
                        }
                        else if (line.StartsWith("P.output.column"))
                        {
                            outputColumns = m.ParseArray(line);
                        }
                        else if (line.StartsWith("soubor"))
                            break;
                    }
                }
                //set the input Variables
                for (int i = 0; i < inputVariables.Count; i++)
                {
                    m.InputVariables.Add(VariableFromString(inputVariables[i], inputUnits[i], inputIntervals[i], inputColumns[i]));
                }
                //set the output variables
                for (int i = 0; i < outputVariables.Count; i++)
                {
                    m.OutputVariables.Add(VariableFromString(outputVariables[i], outputUnits[i], outputIntervals[i], outputColumns[i]));
                }
                //set the input files
                for (int i = 0; i < inputFiles.Count; i++)
                {
                    m.InputFiles.Add(inputFiles[i]);
                }
                if (m.InputFiles.Count > 0 && m.InputFiles.Count < m.InputVariables.Count)
                {
                    while (m.InputFiles.Count < m.InputVariables.Count)
                    {
                        m.InputFiles.Add(m.InputFiles[m.InputFiles.Count - 1]);
                    }
                }

                //set the output files
                for (int i = 0; i < outputFiles.Count; i++)
                {
                    m.OutputFiles.Add(outputFiles[i]);
                }
            }
            catch
            {
                return m;
            }
            return m;
        }

        private static Variable VariableFromString(string name, string unitsName, string timeInterval, string columnName)
        {
            Variable v = new Variable();
            v.IsRegular = true;
            v.IsCategorical = false;
            v.DataType = "Average";
            v.GeneralCategory = "Climate";
            v.IsCategorical = false;
            v.IsRegular = true;
            v.Name = name;
            v.NoDataValue = -9999.0;
            v.Speciation = columnName;
            v.Code = string.Format("drought:{0}", name.Replace(" ", "_"));

            SetVariableUnit(v, unitsName);
            SetTimeSupport(v, timeInterval);
            SetVariableProperties(v, name);

            return v;
        }

        private static Variable VariableFromString(string name, string unitsName, string timeInterval)
        {
            Variable v = new Variable();
            v.IsRegular = true;
            v.IsCategorical = false;
            v.DataType = "Average";
            v.GeneralCategory = "Climate";
            v.IsCategorical = false;
            v.IsRegular = true;
            v.Name = name;
            v.NoDataValue = -9999.0;
            v.Speciation = "Not Applicable";
            v.Code = string.Format("drought:{0}",name.Replace(" ","_"));

            SetVariableUnit(v, unitsName);
            SetTimeSupport(v, timeInterval);
            SetVariableProperties(v, name);

            return v;
        }

        private static void SetVariableUnit(Variable v, string unitsName)
        {
            if (v.VariableUnit == null)
                v.VariableUnit = Unit.Unknown;

            switch (unitsName.ToLower())
            {
                case "celsius":
                case "degc":
                case "degreescelsius":
                case "°c":
                    v.VariableUnit.Abbreviation = "degC";
                    v.VariableUnit.Name = "Degrees Celsius";
                    v.VariableUnit.UnitsType = "temperature";
                    break;
                case "fahrenheit":
                    v.VariableUnit.Abbreviation = "F";
                    v.VariableUnit.Name = "Degrees Fahrenheit";
                    v.VariableUnit.UnitsType = "temperature";
                    break;
                default:
                    v.VariableUnit.Abbreviation = unitsName;
                    v.VariableUnit.Name = unitsName;
                    break;
            }
        }

        private static void SetTimeSupport(Variable v, string timeInterval)
        {
            if (v.TimeUnit == null)
                v.TimeUnit = Unit.UnknownTimeUnit;

            v.TimeSupport = 1.0; //assuming one unit

            switch (timeInterval.ToLower())
            {
                case "day":
                case "d":
                    v.TimeUnit.Name = "Day";
                    v.TimeUnit.Abbreviation = "d";
                    break;
                case "hour":
                case "h":
                    v.TimeUnit.Name = "Hour";
                    v.TimeUnit.Abbreviation = "h";
                    break;
                case "month":
                case "m":
                    v.TimeUnit.Name = "Month";
                    v.TimeUnit.Abbreviation = "M";
                    break;
                case "year":
                case "y":
                    v.TimeUnit.Name = "Year";
                    v.TimeUnit.Abbreviation = "Y";
                    break;
                default:
                    v.TimeUnit.Name = timeInterval;
                    v.TimeUnit.Abbreviation = timeInterval;
                    break;
            }
        }

        private static void SetVariableProperties(Variable v, string variableName)
        {
            switch (variableName.ToLower())
            {
                case "temperature":
                case "t":
                    v.SampleMedium = "Air";
                    v.VariableUnit.UnitsType = "Temperature";
                    break;
                case "precipitation":
                case "sra":
                case "srazky":
                    v.SampleMedium = "Precipitation";
                    v.VariableUnit.UnitsType = "Precipitation";
                    break;
                case "discharge":
                case "streamflow":
                case "prutok":
                    v.SampleMedium = "Water";
                    v.VariableUnit.UnitsType = "Flow";
                    break;
                default:
                    v.SampleMedium = "Unknown";
                    v.VariableUnit.UnitsType = "Unknown";
                    break;
            }
        }

        private static string ParseParameter(string line)
        {
            int indexOne = line.IndexOf("<-");
            if (indexOne > 0)
                indexOne += 1;
            if (indexOne < 0)
                indexOne = line.IndexOf("=");
            if (indexOne < 0)
                return string.Empty;

            string subLineOne = line.Substring(indexOne + 1);
            subLineOne = subLineOne.Replace("\"", "'");
            int indexTwo = subLineOne.IndexOf("'");
            string subLineTwo = subLineOne.Substring(indexTwo + 1);
            int indexThree = subLineTwo.IndexOf("'");
            string subLineThree = subLineTwo.Substring(0, indexThree);

            if (subLineThree.Length > 0)
                return subLineThree;
            else
                return string.Empty;          
        }

        private List<string> ParseArray(string line)
        {
            List<string> outList = new List<string>();

            if (line.Contains("c(") && line.Contains(")"))
            {
                int indexOne = line.IndexOf("c(") + 2;
                string subLine = line.Substring(indexOne);
                int indexTwo = subLine.IndexOf(")");
                string subLineTwo = subLine.Substring(0, indexTwo);

                string[] vals = subLineTwo.Split(new char[] { ',' });
                foreach (string val in vals)
                {
                    string v2 = val.Trim();
                    if (v2.Length > 2)
                        outList.Add(v2.Substring(1, v2.Length - 2));
                    else
                        outList.Add(String.Empty);
                }
            }
            return outList;     
        }

        /// <summary>
        /// True if this is a valid drought analysis method
        /// </summary>
        public bool IsValidMethod()
        {
            return (File.Exists(RScriptPath) && InputVariables.Count > 0 && OutputVariables.Count > 0);
        }

        public override string ToString()
        {
            //return Path.GetFileName(RScriptPath);
            return MethodName;
        }

        /// <summary>
        /// Type of drought
        /// </summary>
        public DroughtType TypeOfDrought { get; set; }
        
        /// <summary>
        /// Name of method
        /// </summary>
        public string MethodName { get; set; }

        /// <summary>
        /// Description of method
        /// </summary>
        public string MethodDescription { get; set; }

        /// <summary>
        /// The path to the R-Script
        /// </summary>
        public string RScriptPath { get; set; }
        
        /// <summary>
        /// Input variables
        /// </summary>
        public IList<Variable> InputVariables { get; set; }

        public IList<string> InputFiles { get; set; }

        public IList<string> OutputFiles { get; set; }

        public IList<VariableDisplayInfo> InputVariablesView
        {
            get
            {
                var resultsView = new List<VariableDisplayInfo>();
                for (int i = 0; i < InputVariables.Count; i++ )
                {
                    resultsView.Add(new VariableDisplayInfo(InputVariables[i], InputFiles[i]));
                }
                return resultsView;
            }
        }

        public IList<VariableDisplayInfo> OutputVariablesView
        {
            get
            {
                var resultsView = new List<VariableDisplayInfo>();
                for (int i=0; i< OutputVariables.Count; i++)
                {
                    resultsView.Add(new VariableDisplayInfo(OutputVariables[i], OutputFiles[i] ));
                }
                return resultsView;
            }
        }

        /// <summary>
        /// Output variables
        /// </summary>
        public IList<Variable> OutputVariables { get; set; }
    }
}
